<!DOCTYPE html>
<link rel="help" src="https://github.com/w3c/csswg-drafts/pull/5666">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
  #scrollers {
    overflow: hidden;
    height: 0px;
  }
  #scrollers > div {
    overflow: scroll;
    width: 100px;
    height: 100px;
  }
  #scrollers > div > div {
    height: 200px;
  }
  @keyframes expand {
    from { width: 100px; }
    to { width: 200px; }
  }
  @scroll-timeline timeline1 {
    source: selector(#scroller1);
    orientation: auto;
    time-range: 1e10s;
    start: 0px;
    end: 100px;
  }
  @scroll-timeline timeline2 {
    source: selector(#scroller2);
    orientation: auto;
    time-range: 1e10s;
    start: 0px;
    end: 100px;
  }
  @scroll-timeline timeline3 {
    source: selector(#scroller3);
    orientation: auto;
    time-range: 1e10s;
    start: 0px;
    end: 100px;
  }
  #element {
    width: 0px;
    height: 20px;
    animation-name: expand;
    animation-duration: 1e10s;
    animation-timing-function: linear;
    animation-timeline: timeline1;
  }
  /* Ensure stable expectations if feature is not supported */
  @supports not (animation-timeline:foo) {
    #element { animation-play-state: paused; }
  }
</style>
<div id=scrollers>
  <div id=scroller1><div></div></div>
  <div id=scroller2><div></div></div>
  <div id=scroller3><div></div></div>
  <div id=scroller4><div></div></div>
</div>
<div id=container></div>
<script>
  // Force layout of scrollers.
  scroller1.offsetTop;
  scroller2.offsetTop;
  scroller3.offsetTop;
  scroller4.offsetTop;

  scroller1.scrollTop = 20;
  scroller2.scrollTop = 40;
  scroller3.scrollTop = 60;
  scroller4.scrollTop = 80;

  // Create #element in #container, run |func|, then clean up afterwards.
  function test_animation_timeline(func, description) {
    promise_test(async () => {
      try {
        let element = document.createElement('element');
        element.setAttribute('id', 'element');
        container.append(element);
        await func();
      } finally {
        while (container.firstChild)
          container.firstChild.remove();
      }
    }, description);
  }

  test_animation_timeline(async () => {
    await waitForNextFrame();
    assert_equals(getComputedStyle(element).width, '120px');
    element.style = 'animation-timeline:timeline2';
    assert_equals(getComputedStyle(element).width, '140px');
  }, 'Changing animation-timeline changes the timeline (sanity check)');

  test_animation_timeline(async () => {
    await waitForNextFrame();
    assert_equals(getComputedStyle(element).width, '120px');

    // Set a (non-CSS) ScrollTimeline on the CSSAnimation.
    let timeline4 = new ScrollTimeline({
      scrollSource: scroller4,
      timeRange: 1e13,
      startScrollOffset: CSS.px(0),
      endScrollOffset: CSS.px(100)
    });

    element.getAnimations()[0].timeline = timeline4;
    assert_equals(getComputedStyle(element).width, '180px');

    // Changing the animation-timeline property should have no effect.
    element.style = 'animation-timeline:timeline2';
    assert_equals(getComputedStyle(element).width, '180px');
  }, 'animation-timeline ignored after setting timeline with JS (ScrollTimeline from JS)');

  test_animation_timeline(async () => {
    await waitForNextFrame();
    assert_equals(getComputedStyle(element).width, '120px');
    let animation = element.getAnimations()[0];
    let timeline1 = animation.timeline;

    element.style = 'animation-timeline:timeline2';
    assert_equals(getComputedStyle(element).width, '140px');

    animation.timeline = timeline1;
    assert_equals(getComputedStyle(element).width, '120px');

    // Should have no effect.
    element.style = 'animation-timeline:timeline3';
    assert_equals(getComputedStyle(element).width, '120px');
  }, 'animation-timeline ignored after setting timeline with JS (ScrollTimeline from CSS)');

  test_animation_timeline(async () => {
    await waitForNextFrame();
    assert_equals(getComputedStyle(element).width, '120px');
    element.getAnimations()[0].timeline = document.timeline;

    // (The animation continues from where the previous timeline left it).
    assert_equals(getComputedStyle(element).width, '120px');

    // Changing the animation-timeline property should have no effect.
    element.style = 'animation-timeline:timeline2';
    assert_equals(getComputedStyle(element).width, '120px');
  }, 'animation-timeline ignored after setting timeline with JS (document timeline)');

  test_animation_timeline(async () => {
    await waitForNextFrame();
    assert_equals(getComputedStyle(element).width, '120px');
    element.getAnimations()[0].timeline = null;
    assert_equals(getComputedStyle(element).width, '0px');

    // Changing the animation-timeline property should have no effect.
    element.style = 'animation-timeline:timeline2';
    assert_equals(getComputedStyle(element).width, '0px');
  }, 'animation-timeline ignored after setting timeline with JS (null)');

</script>
